#include <iostream>
#include <stdio.h>

class Base
{
	public:
		int x;
		int y;

		virtual void function_1()
		{
			printf("function_1...\n");
		}

		virtual void function_2()
		{
			printf("function_2...\n");
		}
};

int main(int argc, char ** argv)
{
	Base base;
	Base *pb;

	printf("sizeof = %ld\n",  sizeof(class Base));		/* 4 */

	printf("base address is 0x%x.\n", &base);		// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前四个字节。
	printf("base virtual table address is:0x%x\n", *(int*)&base);   	// 虚表地址。取this指针的前4个字节。*(char*)&base为取一个字节。	

	typedef void(*pfunction)(void);
	
	pfunction pf;

	int temp = *((int*)(*(int*)&base) + 0);		// 取地址表里面的第一个值
							// *(int*)&base) 是虚表地址
							// (int*) 转换成一个int*类型。
							// *((int*)(*(int*)&base) + 0)， 加的是0，即取第一个4字节值。
	pf = (pfunction)*((int*)(*(int*)&base) + 0);	// 或这样，取地址表里面的第一个值
	pf();						// function_1

	pf = (pfunction)*((int*)(*(int*)&base) + 1);	// 取第二个4字节值
	pf();						// function_2

	for (int i = 0; i < 2; i++) {
		pf = (pfunction)*((int*)(*(int*)&base) + i);
		pf();
	}
}

/*
	sizeof = 12
	base address is 0xbffe7b28.
	base virtual table address is:0x80488d8
	function_1...
	function_2...
	function_1...
	function_2...
 */


#if 0

1.类对象的首地址里面存的第一个4字节才是虚表的地址。即this指针里面存的第一个4字节就是虚表的地址。
2.虚表的地址不是 this 指针，是 this 指针的前四个字节。
3.虚表的个数是编译的时候确定的，有几个 virtual 存几个。

int main(int argc, char ** argv)
{
 8048670:	55                   	push   %ebp
 8048671:	89 e5                	mov    %esp,%ebp
 8048673:	83 e4 f0             	and    $0xfffffff0,%esp
 8048676:	83 ec 30             	sub    $0x30,%esp
	Base base;
 8048679:	8d 44 24 18          	lea    0x18(%esp),%eax
 804867d:	89 04 24             	mov    %eax,(%esp)
 8048680:	e8 1d 01 00 00       	call   80487a2 <_ZN4BaseC1Ev>
	Base *pb;

	printf("base address is %x.\n", &base);		// this 指针，是堆栈里面的地址。虚表的地址不是this指针，是this指针的前四个字节。
 8048685:	8d 44 24 18          	lea    0x18(%esp),%eax
 8048689:	89 44 24 04          	mov    %eax,0x4(%esp)
 804868d:	c7 04 24 60 88 04 08 	movl   $0x8048860,(%esp)
 8048694:	e8 a7 fe ff ff       	call   8048540 <printf@plt>
	printf("base virtual table address is:%x\n", *(int*)&base);   	// 虚表地址。取this指针的前4个字节。*(char*)&base为取一个字节。	
 8048699:	8d 44 24 18          	lea    0x18(%esp),%eax
 804869d:	8b 00                	mov    (%eax),%eax
 804869f:	89 44 24 04          	mov    %eax,0x4(%esp)
 80486a3:	c7 04 24 78 88 04 08 	movl   $0x8048878,(%esp)
 80486aa:	e8 91 fe ff ff       	call   8048540 <printf@plt>

	typedef void(*pfunction)(void);
	
	pfunction pf;

	int temp = *((int*)(*(int*)&base) + 0);		// 取地址表里面的第一个值
 80486af:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486b3:	8b 00                	mov    (%eax),%eax
 80486b5:	8b 00                	mov    (%eax),%eax
 80486b7:	89 44 24 28          	mov    %eax,0x28(%esp)
							// *(int*)&base) 是虚表地址
							// (int*) 转换成一个int*类型。
							// *((int*)(*(int*)&base) + 0)， 加的是0，即取第一个4字节值。
	pf = (pfunction)*((int*)(*(int*)&base) + 0);	// 或这样，取地址表里面的第一个值
 80486bb:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486bf:	8b 00                	mov    (%eax),%eax
 80486c1:	8b 00                	mov    (%eax),%eax
 80486c3:	89 44 24 24          	mov    %eax,0x24(%esp)
	pf();						// function_1
 80486c7:	8b 44 24 24          	mov    0x24(%esp),%eax
 80486cb:	ff d0                	call   *%eax

	pf = (pfunction)*((int*)(*(int*)&base) + 1);	// 取第二个4字节值
 80486cd:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486d1:	8b 00                	mov    (%eax),%eax
 80486d3:	83 c0 04             	add    $0x4,%eax
 80486d6:	8b 00                	mov    (%eax),%eax
 80486d8:	89 44 24 24          	mov    %eax,0x24(%esp)
	pf();						// function_2
 80486dc:	8b 44 24 24          	mov    0x24(%esp),%eax
 80486e0:	ff d0                	call   *%eax

	for (int i = 0; i < 2; i++) {
 80486e2:	c7 44 24 2c 00 00 00 	movl   $0x0,0x2c(%esp)
 80486e9:	00 
 80486ea:	eb 24                	jmp    8048710 <main+0xa0>
		pf = (pfunction)*((int*)(*(int*)&base) + i);
 80486ec:	8b 44 24 2c          	mov    0x2c(%esp),%eax
 80486f0:	8d 14 85 00 00 00 00 	lea    0x0(,%eax,4),%edx
 80486f7:	8d 44 24 18          	lea    0x18(%esp),%eax
 80486fb:	8b 00                	mov    (%eax),%eax
 80486fd:	01 d0                	add    %edx,%eax
 80486ff:	8b 00                	mov    (%eax),%eax
 8048701:	89 44 24 24          	mov    %eax,0x24(%esp)
		pf();
 8048705:	8b 44 24 24          	mov    0x24(%esp),%eax
 8048709:	ff d0                	call   *%eax
	pf();						// function_1

	pf = (pfunction)*((int*)(*(int*)&base) + 1);	// 取第二个4字节值
	pf();						// function_2

	for (int i = 0; i < 2; i++) {
 804870b:	83 44 24 2c 01       	addl   $0x1,0x2c(%esp)
 8048710:	83 7c 24 2c 01       	cmpl   $0x1,0x2c(%esp)
 8048715:	7e d5                	jle    80486ec <main+0x7c>
		pf = (pfunction)*((int*)(*(int*)&base) + i);
		pf();
	}
}
 8048717:	b8 00 00 00 00       	mov    $0x0,%eax
 804871c:	c9                   	leave  
 804871d:	c3                   	ret  


#endif
